package com.atguigu.tingshu.common.minio.service.impl;

import com.atguigu.tingshu.common.minio.config.MinioProperties;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.minio.service.FileUploadService;
import com.atguigu.tingshu.common.util.MD5;
import io.minio.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;

/**
 * Author:huzhongkui
 * Date: 2025-03-12 星期三 11:41:49
 * Description:
 */

//@Service
@Slf4j
public class FileUploadServiceImpl implements FileUploadService {

    @Autowired
    private MinioProperties minioProperties;

    @Autowired
    private MinioClient minioClient;

    @Override
    public String fileUploadService(MultipartFile file) {

        //  byte[] = {'a','b','12','96'}
        //  byte[] = {'a','b','12','96'}
        String objectKey = "";
        String originalFilename = file.getOriginalFilename();
        try {
            //这段代码的核心目的是通过MD5加密和文件后缀拼接的方式，生成一个唯一的对象键（objectKey），
            // 以确保上传到MinIO的文件名唯一且保留原文件格式。
            byte[] bytes = file.getBytes();
            /**
             * 将字节数组转换为字符串形式（s）。
             * 注意：这里假设文件内容可以被正确解析为字符串，
             * 但如果文件是二进制文件（如图片、视频等），可能会导致乱码或错误。
             */
            String s = new String(bytes);
//            String s = bytes.toString();  // 地址值 //回的是字节数组对象的内存地址值，而不是文件的实际内容，因此这里被注释掉了。
            //使用MD5算法对字符串 s 进行加密，生成一个固定长度的哈希值作为文件名的前缀（prefix）。这确保了文件名的唯一性。
            String prefix = MD5.encrypt(s);
            //从原始文件名（originalFilename）中提取文件的后缀名（如 .jpg、.txt 等），确保上传到MinIO的文件保留原有的格式。
            String suffix = originalFilename.substring(originalFilename.lastIndexOf("."), originalFilename.length());
            objectKey = prefix + suffix; //将MD5加密后的前缀和文件后缀拼接起来，形成一个唯一的对象键（objectKey），
            // 用于标识该文件在MinIO存储中的位置。
            // 1.检查该上传的文件是否在minio中
            StatObjectArgs.Builder builder = StatObjectArgs.builder();
            StatObjectArgs statObjectArgs = builder
                    .bucket(minioProperties.getBucketName())
                    .object(objectKey)
                    .build();
            minioClient.statObject(statObjectArgs);// 如果不文件存在，则抛出异常，表示文件已经存在。
            // 2. minio有 则直接返回
            return minioProperties.getEndpointUrl() + "/" + minioProperties.getBucketName() + "/" + objectKey;
        } catch (IOException e) {
            log.error("上传的文件不存在原因：{}", e.getMessage());
            throw new GuiguException(201, "该文件不存在");
        } catch (Exception e) {

            // 3.minio没有 可以上传
            log.info("该文件在桶中不存在，可以上传到minio中");
            try {
                // 4.将文件上传到桶中
                PutObjectArgs.Builder putObjectArgsBuilder = PutObjectArgs.builder();
                PutObjectArgs putObjectArgs = putObjectArgsBuilder
                        .bucket(minioProperties.getBucketName())
                        .object(objectKey)
                        .stream(file.getInputStream(), file.getSize(), -1)
                        .build();
                minioClient.putObject(putObjectArgs);
                log.info("上传文件到minio成功");
                // 5.将图片地址返回
                return minioProperties.getEndpointUrl() + "/" + minioProperties.getBucketName() + "/" + objectKey;
            } catch (Exception subE) {
                log.error("上传文件到minio中失败：{}", subE.getMessage());
                throw new GuiguException(201, "上传文件到minio中失败");
            }
        }
    }
}
